home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / pyatspi / accessible.py < prev    next >
Text File  |  2009-10-19  |  22KB  |  688 lines

  1. '''
  2. Creates functions at import time that are mixed into the 
  3. Accessibility.Accessible base class to make it more Pythonic.
  4.  
  5. Based on public domain code originally posted at 
  6. U{http://wwwx.cs.unc.edu/~parente/cgi-bin/RuntimeClassMixins}.
  7.  
  8. @var _ACCESSIBLE_CACHE: Pairs hash values for accessible objects to 
  9.   L{_PropertyCache} bags. We do not store actual accessibles in the dictionary
  10.   because that would +1 their ref counts and cause __del__ to never be called
  11.   which is the method we rely on to properly invalidate cache entries.
  12. @type _ACCESSIBLE_CACHE: dictionary
  13. @var _CACHE_LEVEL: Current level of caching enabled. Checked dynamically by
  14.   L{_AccessibleMixin}
  15. @type _CACHE_LEVEL: integer
  16.  
  17. @author: Peter Parente
  18. @organization: IBM Corporation
  19. @copyright: Copyright (c) 2005, 2007 IBM Corporation
  20. @license: LGPL
  21.  
  22. This library is free software; you can redistribute it and/or
  23. modify it under the terms of the GNU Library General Public
  24. License as published by the Free Software Foundation; either
  25. version 2 of the License, or (at your option) any later version.
  26.  
  27. This library is distributed in the hope that it will be useful,
  28. but WITHOUT ANY WARRANTY; without even the implied warranty of
  29. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  30. Library General Public License for more details.
  31.  
  32. You should have received a copy of the GNU Library General Public
  33. License along with this library; if not, write to the
  34. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  35. Boston, MA 02111-1307, USA.
  36.  
  37. Portions of this code originally licensed and copyright (c) 2005, 2007
  38. IBM Corporation under the BSD license, available at
  39. U{http://www.opensource.org/licenses/bsd-license.php}
  40. '''
  41. import new
  42. import types
  43. import ORBit
  44. import Accessibility
  45. import constants
  46. import utils
  47. import registry
  48. import weakref
  49.  
  50. _ACCESSIBLE_CACHE = weakref.WeakValueDictionary()
  51. _ACCESSIBLE_USER_DATA = weakref.WeakValueDictionary()
  52. _CACHE_LEVEL = None
  53.  
  54. class _PropertyCache:
  55.   def wipe(self):
  56.       self.__dict__ = {}
  57.  
  58. class _UserData:
  59.   value = None
  60.  
  61. def getCacheLevel():
  62.   '''
  63.   Gets the current level of caching.
  64.   
  65.   @return: None indicating no caching is in effect. 
  66.     L{constants.CACHE_INTERFACES} indicating all interface query results are
  67.     cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible
  68.     properties are cached.
  69.   @rtype: integer
  70.   '''
  71.   return _CACHE_LEVEL
  72.  
  73. def setCacheLevel(val):
  74.   '''
  75.   Sets the desired level of caching for all accessible objects created after
  76.   this function is invoked. Immediately clears the current accessible cache.
  77.   
  78.   @param val: None indicating no caching is in effect. 
  79.     L{constants.CACHE_INTERFACES} indicating all interface query results are
  80.     cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible
  81.     properties are cached plus all interfaces.
  82.   @type val: integer
  83.   '''
  84.   global _CACHE_LEVEL
  85.   if _CACHE_LEVEL != val:
  86.     # empty our accessible cache  
  87.     _ACCESSIBLE_CACHE.clear()
  88.     # need to register/unregister for listeners depending on caching level
  89.     if val == constants.CACHE_PROPERTIES:
  90.       r = registry.Registry()
  91.       r.registerEventListener(_updateCache, *constants.CACHE_EVENTS)
  92.     else:
  93.       r = registry.Registry()
  94.       r.deregisterEventListener(_updateCache, *constants.CACHE_EVENTS)
  95.   _CACHE_LEVEL = val
  96.   
  97. def clearCache():
  98.   '''Forces a clear of the entire cache.'''
  99.   _ACCESSIBLE_CACHE.clear()
  100.   
  101. def printCache(template='%s'):
  102.   '''
  103.   Prints the contents of the cache.
  104.   
  105.   @param template: Format string to use when printing
  106.   @type template: string
  107.   '''
  108.   print template % _ACCESSIBLE_CACHE
  109.  
  110. def _updateCache(event):
  111.   '''
  112.   Invalidates an entry in the cache when the hash value of a source of an event
  113.   matches an entry in the cache.
  114.   
  115.   @param event: One of the L{constants.CACHE_EVENTS} event types
  116.   @type event: L{event.Event}
  117.   '''
  118.   try:
  119.     _ACCESSIBLE_CACHE[hash(event.source)].wipe()
  120.   except KeyError:
  121.     return
  122.  
  123. def _getAndCache(acc, value_name, get_method):
  124.   '''
  125.   If property caching is enabled, use the cached proprty, or get the 
  126.   property and cache it. If property caching is disabled, simply get the 
  127.   property.
  128.  
  129.   @param value_name: The name of the value, like 'role' or 'description'.
  130.   @type value_name: string
  131.   @param get_method: Method used to get the property, should not have any 
  132.   arguments.
  133.   @type get_method: callable
  134.   @return: Value of property we are retrieving.
  135.   @rtype: object
  136.   '''
  137.   if _CACHE_LEVEL != constants.CACHE_PROPERTIES:
  138.     return get_method()
  139.     
  140.   cache = _ACCESSIBLE_CACHE
  141.   h = hash(acc)
  142.  
  143.   try:
  144.     pc = acc._property_cache
  145.   except AttributeError:
  146.     try:
  147.       pc = cache[h]
  148.     except KeyError:
  149.       # no cached info for this accessible yet
  150.       pc = _PropertyCache()
  151.       cache[h] = pc
  152.     acc._property_cache = pc  
  153.     
  154.   try:
  155.     value = getattr(pc, value_name)
  156.   except AttributeError:
  157.     # no cached property of this type
  158.     value = get_method()
  159.     setattr(pc, value_name, value)
  160.     
  161.   return value
  162.   
  163.  
  164. def _makeQuery(interface):
  165.   '''
  166.   Builds a function querying to a specific interface and returns it.
  167.   
  168.   @param interface: Class representing an AT-SPI interface
  169.   @type interface: class
  170.   @return: Function querying to the given interface
  171.   @rtype: function
  172.   '''
  173.   def _inner(self):
  174.     '''
  175.     Queries an object for another interface.
  176.   
  177.     @return: An object with the desired interface
  178.     @rtype: object
  179.     @raise NotImplementedError: When the desired interface is not supported    
  180.     '''
  181.     iid = utils.getInterfaceIID(interface)
  182.     try:
  183.       i = self._icache[iid]
  184.     except KeyError:
  185.       # interface not cached
  186.       caching = True
  187.     except AttributeError:
  188.       # determine if we're caching
  189.       caching = _CACHE_LEVEL is not None
  190.       if caching:
  191.         # initialize the cache
  192.         self._icache = {}
  193.     else:
  194.       # check if our cached result was an interface, or an indicator that the
  195.       # interface is not supported
  196.       if i is None:
  197.         raise NotImplementedError
  198.       else:
  199.         return i
  200.  
  201.     try:
  202.       # do the query remotely
  203.       i = self.queryInterface(iid)
  204.       if i is not None:
  205.         i = i._narrow(interface)
  206.     except Exception, e:
  207.       raise LookupError(e)      
  208.     if i is None:
  209.       # cache that the interface is not supported
  210.       if caching:
  211.         self._icache[iid] = None
  212.       raise NotImplementedError
  213.     
  214.     if caching:
  215.       # cache the narrow'ed result, but only if we're caching for this object
  216.       self._icache[iid] = i
  217.     return i
  218.   
  219.   return _inner
  220.  
  221. def _makeExceptionHandler(func):
  222.   '''
  223.   Builds a function calling the one it wraps in try/except statements catching
  224.   CORBA exceptions.
  225.   
  226.   @return: Function calling the method being wrapped
  227.   @rtype: function
  228.   '''
  229.   def _inner(self, *args, **kwargs):
  230.     try:
  231.       # try calling the original func
  232.       return func(self, *args, **kwargs)
  233.     except ORBit.CORBA.NO_IMPLEMENT, e:
  234.       # raise Python exception
  235.       raise NotImplementedError(e)
  236.     except ORBit.CORBA.Exception, e:
  237.       # raise Python exception
  238.       raise LookupError(e)
  239.   return _inner
  240.  
  241. def _mixInterfaces(cls, interfaces):
  242.   '''
  243.   Add methods for querying to interfaces other than the base accessible to
  244.   the given class.
  245.   
  246.   @param cls: Class to mix interface methods into
  247.   @type cls: class
  248.   @param interfaces: Classes representing AT-SPI interfaces
  249.   @type interfaces: list of class
  250.   '''
  251.   # create functions in this module for all interfaces listed in constants
  252.   for interface in interfaces:
  253.     # build name of converter from the name of the interface
  254.     name = 'query%s' % utils.getInterfaceName(interface)
  255.     # build a function that queries to the given interface
  256.     func = _makeQuery(interface)
  257.     # build a new method that is a clone of the original function
  258.     method = new.function(func.func_code, func.func_globals, name, 
  259.                           func.func_defaults, func.func_closure)
  260.     # add the method to the given class
  261.     setattr(cls, name, method)
  262.  
  263. def _mixExceptions(cls):
  264.   '''
  265.   Wraps all methods and properties in a class with handlers for CORBA 
  266.   exceptions.
  267.   
  268.   @param cls: Class to mix interface methods into
  269.   @type cls: class
  270.   '''
  271.   # get a method type as a reference from a known method
  272.   method_type = Accessibility.Accessible.getRole.__class__
  273.   # loop over all names in the new class
  274.   for name in cls.__dict__.keys():
  275.     obj = cls.__dict__[name]
  276.     # check if we're on a protected or private method
  277.     if name.startswith('_'):
  278.       continue
  279.     # check if we're on a method
  280.     elif isinstance(obj, method_type):
  281.       # wrap the function in an exception handler
  282.       method = _makeExceptionHandler(obj)
  283.       # add the wrapped function to the class
  284.       setattr(cls, name, method)
  285.     # check if we're on a property
  286.     elif isinstance(obj, property):
  287.       # wrap the getters and setters
  288.       if obj.fget:
  289.         func = getattr(cls, obj.fget.__name__)
  290.         getter = _makeExceptionHandler(func)
  291.       else:
  292.         getter = None
  293.       if obj.fset:
  294.         func = getattr(cls, obj.fset.__name__)
  295.         setter = _makeExceptionHandler(func)
  296.       else:
  297.         setter = None
  298.       setattr(cls, name, property(getter, setter))
  299.  
  300. def _mixClass(cls, new_cls, ignore=[]):
  301.   '''
  302.   Adds the methods in new_cls to cls. After mixing, all instances of cls will
  303.   have the new methods. If there is a method name clash, the method already in
  304.   cls will be prefixed with '_mix_' before the new method of the same name is 
  305.   mixed in.
  306.   
  307.   @note: _ is not the prefix because if you wind up with __ in front of a 
  308.   variable, it becomes private and mangled when an instance is created. 
  309.   Difficult to invoke from the mixin class.
  310.  
  311.   @param cls: Existing class to mix features into
  312.   @type cls: class
  313.   @param new_cls: Class containing features to add
  314.   @type new_cls: class
  315.   @param ignore: Ignore these methods from the mixin
  316.   @type ignore: iterable
  317.   '''
  318.   # loop over all names in the new class
  319.   for name, func in new_cls.__dict__.items():
  320.     if name in ignore:
  321.       continue
  322.     if isinstance(func, types.FunctionType):
  323.       # build a new function that is a clone of the one from new_cls
  324.       method = new.function(func.func_code, func.func_globals, name, 
  325.                             func.func_defaults, func.func_closure)
  326.       try:
  327.         # check if a method of the same name already exists in the target
  328.         old_method = getattr(cls, name)
  329.       except AttributeError:
  330.         pass
  331.       else:
  332.         # rename the old method so we can still call it if need be
  333.         setattr(cls, '_mix_'+name, old_method)
  334.       # add the clone to cls
  335.       setattr(cls, name, method)
  336.     elif isinstance(func, staticmethod):
  337.       try:
  338.         # check if a method of the same name already exists in the target
  339.         old_method = getattr(cls, name)
  340.       except AttributeError:
  341.         pass
  342.       else:
  343.         # rename the old method so we can still call it if need be
  344.         setattr(cls, '_mix_'+name, old_method)
  345.       setattr(cls, name, func)
  346.     elif isinstance(func, property):
  347.       try:
  348.         # check if a method of the same name already exists in the target
  349.         old_prop = getattr(cls, name)
  350.       except AttributeError:
  351.         pass
  352.       else:
  353.         # IMPORTANT: We save the old property before overwriting it, even 
  354.         # though we never end up calling the old prop from our mixin class.
  355.         # If we don't save the old one, we seem to introduce a Python ref count
  356.         # problem where the property get/set methods disappear before we can
  357.         # use them at a later time. This is a minor waste of memory because
  358.         # a property is a class object and we only overwrite a few of them.
  359.         setattr(cls, '_mix_'+name, old_prop)
  360.       setattr(cls, name, func)
  361.  
  362. class _AccessibleMixin(object):
  363.   '''
  364.   Defines methods to be added to the Accessibility.Accessible class. The
  365.   features defined here will be added to the Accessible class at run time so
  366.   that all instances of Accessible have them (i.e. there is no need to
  367.   explicitly wrap an Accessible in this class or derive a new class from it.)
  368.   
  369.   @cvar SLOTTED_CLASSES: Mapping from raw Accessibility class to a new class
  370.     having the slots defined by L{SLOTS}
  371.   @type SLOTTED_CLASSES: dictionary
  372.   @cvar SLOTS: All slots to create
  373.   @type SLOTS: tuple
  374.   '''
  375.   SLOTTED_CLASSES = {}
  376.   SLOTS = ('_icache', '_property_cache', '_user_data')
  377.   
  378.   def __new__(cls):
  379.     '''
  380.     Creates a new class mimicking the one requested, but with extra named 
  381.     defined in __slots__. The _cache attribute is used internally for interface
  382.     caching. The user_data field may be populated with whatever data structure
  383.     a client wishes to use. Neither is set to a default value by default.
  384.     
  385.     Note that we can't simply mix __slots__ into this class because __slots__
  386.     has an effect only at class creation time. 
  387.     
  388.     We also do not completely obliterate __slots__ to allow __dict__ to be
  389.     instantiated as normal as reducing the initialization and memory overhead
  390.     of the millions of accessible objects that are created is a good thing for
  391.     many clients.
  392.     
  393.     @param cls: Accessibility object class
  394.     @type cls: class
  395.     @return: Instance of the new class
  396.     @rtype: object
  397.     '''
  398.     try:
  399.       # check if we've already created a new version of the class
  400.       new_cls = _AccessibleMixin.SLOTTED_CLASSES[cls]
  401.     except KeyError:
  402.       # create the new class if not
  403.       new_cls = type(cls.__name__, (cls,), 
  404.                      {'__module__' : cls.__module__, 
  405.                       '__slots__' : _AccessibleMixin.SLOTS})
  406.       _AccessibleMixin.SLOTTED_CLASSES[cls] = new_cls
  407.     obj = cls._mix___new__(new_cls)
  408.     return obj
  409.   
  410.   def __del__(self):
  411.     '''    
  412.     Decrements the reference count on the accessible object when there are no
  413.     Python references to this object. This provides automatic reference
  414.     counting for AT-SPI objects. Also removes this object from the cache if
  415.     we're caching properties. 
  416.     '''
  417.     try:
  418.       self.unref()
  419.     except Exception:
  420.       pass
  421.     
  422.   def __iter__(self):
  423.     '''
  424.     Iterator that yields one accessible child per iteration. If an exception is
  425.     encountered, None is yielded instead.
  426.     
  427.     @return: A child accessible
  428.     @rtype: Accessibility.Accessible
  429.     '''
  430.     for i in xrange(self.childCount):
  431.       try:
  432.         yield self.getChildAtIndex(i)
  433.       except LookupError:
  434.         yield None
  435.     
  436.   def __str__(self):
  437.     '''
  438.     Gets a human readable representation of the accessible.
  439.     
  440.     @return: Role and name information for the accessible
  441.     @rtype: string
  442.     '''
  443.     try:
  444.       return '[%s | %s]' % (self.getRoleName(), self.name)
  445.     except Exception:
  446.       return '[DEAD]'
  447.     
  448.   def __nonzero__(self):
  449.     '''
  450.     @return: True, always
  451.     @rtype: boolean
  452.     '''
  453.     return True
  454.     
  455.   def __getitem__(self, index):
  456.     '''
  457.     Thin wrapper around getChildAtIndex.
  458.     
  459.     @param index: Index of desired child
  460.     @type index: integer
  461.     @return: Accessible child
  462.     @rtype: Accessibility.Accessible
  463.     '''
  464.     n = self.childCount
  465.     if index >= n:
  466.       raise IndexError
  467.     elif index < -n:
  468.       raise IndexError
  469.     elif index < 0:
  470.       index += n
  471.     return self.getChildAtIndex(index)
  472.   
  473.   def __len__(self):
  474.     '''
  475.     Thin wrapper around childCount.
  476.     
  477.     @return: Number of child accessibles
  478.     @rtype: integer
  479.     '''
  480.     return self.childCount
  481.   
  482.   def _get_user_data(self):
  483.     '''
  484.     Get user_data from global dictionay fo this accessible.
  485.  
  486.     @return: Any data the user assigned, or None.
  487.     @rtype: object
  488.     '''
  489.     global _ACCESSIBLE_USER_DATA
  490.     h = hash(self)
  491.     
  492.     try:
  493.       ud = self._user_data
  494.     except AttributeError:
  495.       try:
  496.         ud = _ACCESSIBLE_USER_DATA[h]
  497.       except KeyError:
  498.         # no cached info for this object yet
  499.         ud = _UserData()
  500.         _ACCESSIBLE_USER_DATA[h] = ud
  501.  
  502.     self._user_data = ud
  503.     return ud.value
  504.  
  505.   def _set_user_data(self, value):
  506.     '''
  507.     Set arbitrary data to user_data.
  508.  
  509.     @param value: Value to set in user_data
  510.     @type value: object
  511.     '''
  512.     global _ACCESSIBLE_USER_DATA
  513.     h = hash(self)
  514.     
  515.     try:
  516.       ud = self._user_data
  517.     except AttributeError:
  518.       try:
  519.         ud = _ACCESSIBLE_USER_DATA[h]
  520.       except KeyError:
  521.         # no cached info for this object yet
  522.         ud = _UserData()
  523.         _ACCESSIBLE_USER_DATA[h] = ud
  524.  
  525.     self._user_data = ud
  526.     ud.value = value
  527.  
  528.   user_data = property(_get_user_data, _set_user_data)
  529.  
  530.   def _get_name(self):
  531.     '''
  532.     Gets the name of the accessible from the cache if it is available, 
  533.     otherwise, fetches it remotely.
  534.     
  535.     @return: Name of the accessible
  536.     @rtype: string
  537.     '''
  538.     return _getAndCache(self, 'name', self._get_name)
  539.  
  540.   name = property(_get_name, Accessibility.Accessible._set_name)
  541.  
  542.   def _get_parent(self):
  543.     '''
  544.     Gets the parent of the accessible from the cache if it is available, 
  545.     otherwise, fetches it remotely.
  546.     
  547.     @return: Parent of the accessible
  548.     @rtype: Accessibility.Accessible
  549.     '''
  550.     return _getAndCache(self, 'parent', self._get_parent)
  551.  
  552.   parent = property(_get_parent)
  553.   
  554.   def getRoleName(self):
  555.     '''
  556.     Gets the unlocalized role name of the accessible from the cache if it is 
  557.     available, otherwise, fetches it remotely.
  558.     
  559.     @return: Role name of the accessible
  560.     @rtype: string
  561.     '''
  562.     return _getAndCache(self, 'rolename', self._mix_getRoleName)
  563.  
  564.   def getRole(self):
  565.     '''
  566.     Gets the role of the accessible from the cache if it is 
  567.     available, otherwise, fetches it remotely.
  568.     
  569.     @return: Role of the accessible
  570.     @rtype: Accessibility.Role
  571.     '''
  572.     return _getAndCache(self, 'role', self._mix_getRole)
  573.  
  574.   def _get_description(self):
  575.     '''    
  576.     Gets the description of the accessible from the cache if it is available,
  577.     otherwise, fetches it remotely.
  578.     
  579.     @return: Description of the accessible
  580.     @rtype: string
  581.     '''
  582.     return _getAndCache(self, 'description', self._get_description)
  583.     
  584.   description = property(_get_description, 
  585.                          Accessibility.Accessible._set_description)
  586.   
  587.   def getIndexInParent(self):
  588.     '''
  589.     Gets the index of this accessible in its parent. Uses the implementation of
  590.     this method provided by the Accessibility.Accessible object, but checks the
  591.     bound of the value to ensure it is not outside the range of childCount 
  592.     reported by this accessible's parent.
  593.     
  594.     @return: Index of this accessible in its parent
  595.     @rtype: integer
  596.     '''
  597.     i = self._mix_getIndexInParent()
  598.     try:
  599.       # correct for out-of-bounds index reporting
  600.       return min(self.parent.childCount-1, i)
  601.     except AttributeError:
  602.       # return sentinel if there is no parent
  603.       return -1
  604.  
  605.   def getApplication(self):
  606.     '''
  607.     Gets the most-parent accessible (the application) of this
  608.     accessible. Tries using the getApplication method introduced in
  609.     AT-SPI 1.7.0 first before resorting to traversing parent links.
  610.     
  611.     @warning: Cycles involving more than the previously traversed accessible 
  612.       are not detected by this code.
  613.     @return: Application object
  614.     @rtype: Accessibility.Application
  615.     '''
  616.     try:
  617.       app = self._mix_getApplication()
  618.     except AttributeError:
  619.       app = None
  620.  
  621.     # Some toolkits (e.g., Java) do not support getApplication yet and
  622.     # will return None as a result.
  623.     #
  624.     if app:
  625.       return app
  626.  
  627.     # If we didn't find anything, traverse up the tree, making sure to
  628.     # attempt to turn the thing we found into an Application object.
  629.     #
  630.     curr = self
  631.     try:
  632.       while curr.parent is not None and (not curr.parent == curr):
  633.         curr = curr.parent
  634.       curr.ref()
  635.       return curr._narrow(Accessibility.Application)
  636.     except:
  637.       return None
  638.  
  639. class _RelationMixin(object):
  640.   '''
  641.   Defines methods to be added to the Relation class. At this time it only
  642.   overrides L{_RelationMixin.getTarget} which by the IDL's standard is
  643.   supposed to return CORBA.Objects but we expect LAccessibility.Accessible
  644.   objects (see http://bugzilla.gnome.org/show_bug.cgi?id=435833). 
  645.   This seems to be a problem especially with the Java implementation of CORBA.
  646.   '''
  647.   def getTarget(self, index):
  648.     '''
  649.     Overrides the regular getTarget to return Accessibility.Accessible
  650.     objects.
  651.  
  652.     @return: The 'nth' target of this Relation.
  653.     @rtype: Accessibility.Accessible
  654.     '''
  655.     target = self._mix_getTarget(index)
  656.     if not isinstance(target, Accessibility.Accessible):
  657.       target = target._narrow(Accessibility.Accessible)
  658.     target.ref()
  659.     return target
  660.  
  661. class _UnrefMixin(object):
  662.   '''
  663.   This mixin addresses the issue we have with unreferencing non-primitives.
  664.   '''
  665.   def __del__(self):
  666.     '''
  667.     Unrefence the instance when Python GCs it. Why do we need this twice?
  668.     '''
  669.     try:
  670.       self.unref()
  671.     except Exception:
  672.       pass
  673.  
  674. # 1. mix the exception handlers into all queryable interfaces
  675. map(_mixExceptions, constants.ALL_INTERFACES)
  676. # 2. mix the exception handlers into other Accessibility objects
  677. map(_mixExceptions, [Accessibility.StateSet])
  678. # 3. mix the new functions
  679. _mixClass(Accessibility.Accessible, _AccessibleMixin,
  680.           ['_get_name', '_get_description', '_get_parent'])
  681. # 4. mix queryInterface convenience methods
  682. _mixInterfaces(Accessibility.Accessible, constants.ALL_INTERFACES)
  683. # 5. mix Relation class
  684. _mixClass(Accessibility.Relation, _RelationMixin)
  685. # 6. mix in neccessary unrefs
  686. map(lambda cls: _mixClass(cls, _UnrefMixin), 
  687.     (Accessibility.StateSet,Accessibility.Relation))
  688.